package com.wiki4j.gdo.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.wiki4j.gdo.GDObject;

public class PathGrammar{
  public static void main(String...args){
    String path="xxx[1][1].ooo[0].abc[1].bcd[0][2].cde.def[0][1][2]";
    PathGrammar grammar=cached(path);
    System.out.println(grammar);
  }

  private List<PathNode> nodes;

  private PathGrammar(List<PathNode> nodes){
    this.nodes=nodes;
  }

  public List<PathNode> getNodes(){
    return nodes;
  }

  @Override
  public String toString(){
    return JSON.toJson(this);
  }

  public void set(GDObject gdo,Object value){
    GDObject con=gdo;
    for(int i=0;i<nodes.size()-1;i++){
      PathNode node=nodes.get(i);
      con=node.con(gdo);
    }
    PathNode last=nodes.get(nodes.size()-1);
    last.set(con,value);
  }

  private static Map<String,PathGrammar> cache=new HashMap<String,PathGrammar>();

  public static PathGrammar cached(String path){
    PathGrammar grammar=cache.get(path);
    if(grammar==null) synchronized(cache){
      grammar=cache.get(path);
      if(grammar==null){
        grammar=parse(path);
        cache.put(path,grammar);
      }
    }
    return grammar;
  }

  public static PathGrammar parse(String path){
    List<PathNode> nodes=new ArrayList<PathNode>();
    char[] chars=path.toCharArray();
    StringBuilder buffer=new StringBuilder();
    String key=null;
    List<Integer> indexes=new ArrayList<Integer>();
    for(int i=0;i<chars.length;i++){
      char c=chars[i];
      switch(c){
        case '.':
          if(key==null){
            key=buffer.toString();
            buffer.setLength(0);
          }
          nodes.add(new PathNode(key,indexes));
          key=null;
          indexes.clear();
          break;
        case '[':
          if(key==null){
            key=buffer.toString();
            buffer.setLength(0);
          }else{
          }
          break;
        case ']':{
          int index=Integer.parseInt(buffer.toString());
          buffer.setLength(0);
          indexes.add(index);
        }
          break;
        default:
          buffer.append(c);
          break;
      }
    }
    if(buffer.length()>0){
      key=buffer.toString();
      buffer.setLength(0);
    }
    if(key!=null){
      nodes.add(new PathNode(key,indexes));
      key=null;
      indexes.clear();
    }
    PathGrammar grammar=new PathGrammar(nodes);
    return grammar;
  }

  public static class PathNode{
    private String key;
    private int[] indexes;

    private PathNode(String key,List<Integer> indexes){
      this.key=key;
      int[] tmp=new int[indexes.size()];
      for(int i=0;i<indexes.size();i++)
        tmp[i]=indexes.get(i);
      this.indexes=tmp;
    }

    public String getKey(){
      return key;
    }

    public int[] getIndexes(){
      return indexes;
    }

    public GDObject con(GDObject gdo){
      return gdo.createGDObject(key,indexes);
    }

    public void set(GDObject gdo,Object value){
      ((GDObjectImpl)gdo).putObject(key,value);
    }
  }
}
